﻿using System;
using System.Collections.Generic;
using FortuneUniverse.Fortune.Algorithms;

namespace FortuneUniverse.Fortune.Dyson
{
  public static class UniverseGen
  {
    public static GalaxyData CreateGalaxy(GameDesc gameDesc)
    {
      int galaxySeed = gameDesc.galaxySeed;
      int starCount = gameDesc.starCount;
      URandom1 urandom1 = new URandom1(galaxySeed);
      List<VectorLF3> tmp_poses;
      UniverseGen.GenerateTempPoses(urandom1.Next(), starCount, 2.0, 2.3, 3.5, 0.18, out tmp_poses);
      GalaxyData galaxy = new GalaxyData();
      galaxy.seed = galaxySeed;
      galaxy.starCount = starCount;
      galaxy.stars = new StarData[starCount];
      if (starCount <= 0)
        return galaxy;
      float num1 = (float) urandom1.NextDouble();
      float num2 = (float) urandom1.NextDouble();
      float num3 = (float) urandom1.NextDouble();
      float num4 = (float) urandom1.NextDouble();
      int num5 = Mathf.CeilToInt((float) (0.00999999977648258 * (double) starCount + (double) num1 * 0.300000011920929));
      int num6 = Mathf.CeilToInt((float) (0.00999999977648258 * (double) starCount + (double) num2 * 0.300000011920929));
      int num7 = Mathf.CeilToInt((float) (0.0160000007599592 * (double) starCount + (double) num3 * 0.400000005960464));
      int num8 = Mathf.CeilToInt((float) (0.0130000002682209 * (double) starCount + (double) num4 * 1.39999997615814));
      int num9 = starCount - num5;
      int num10 = num9 - num6;
      int num11 = num10 - num7;
      int num12 = (num11 - 1) / num8;
      int num13 = num12 / 2;
      for (int index = 0; index < starCount; ++index)
      {
        int seed = urandom1.Next();
        if (index == 0)
        {
          galaxy.stars[index] = StarGen.CreateBirthStar(galaxy, seed);
        }
        else
        {
          ESpectrType needSpectr = ESpectrType.X;
          if (index == 3)
            needSpectr = ESpectrType.M;
          else if (index == num11 - 1)
            needSpectr = ESpectrType.O;
          EStarType needtype = EStarType.MainSeqStar;
          if (index % num12 == num13)
            needtype = EStarType.GiantStar;
          if (index >= num9)
            needtype = EStarType.BlackHole;
          else if (index >= num10)
            needtype = EStarType.NeutronStar;
          else if (index >= num11)
            needtype = EStarType.WhiteDwarf;
          galaxy.stars[index] = StarGen.CreateStar(galaxy, tmp_poses[index], index + 1, seed, needtype, needSpectr);
        }
      }
      StarData[] stars = galaxy.stars;
      for (int index = 0; index < starCount; ++index)
        StarGen.CreateStarPlanets(galaxy, stars[index], gameDesc);
      galaxy.birthPlanetId = 0;
      if (starCount > 0)
      {
        StarData starData = stars[0];
        for (int index = 0; index < starData.planetCount; ++index)
        {
          PlanetData planet = starData.planets[index];
          ThemeProto themeProto = LDB.themes.Select(planet.theme);
          if (themeProto != null && themeProto.Distribute == EThemeDistribute.Birth)
          {
            galaxy.birthPlanetId = planet.id;
            galaxy.birthStarId = starData.id;
            break;
          }
        }
      }
      return galaxy;
    }

    private static void GenerateTempPoses(
      int seed,
      int targetCount,
      double minDist,
      double minStepLen,
      double maxStepLen,
      double flatten,
      out List<VectorLF3> tmp_poses)
    {
      tmp_poses = new List<VectorLF3>();
      int num = 4;
      UniverseGen.RandomPoses(seed, targetCount * num, minDist, minStepLen, maxStepLen, flatten, tmp_poses);
      for (int index = tmp_poses.Count - 1; index >= 0; --index)
      {
        if (index % num != 0)
          tmp_poses.RemoveAt(index);
        if (tmp_poses.Count <= targetCount)
          break;
      }
    }

    private static void RandomPoses(
      int seed,
      int maxCount,
      double minDist,
      double minStepLen,
      double maxStepLen,
      double flatten,
      List<VectorLF3> tmp_poses)
    {
      List<VectorLF3> vectorLf3List = new List<VectorLF3>();
      URandom1 urandom1 = new URandom1(seed);
      double num1 = urandom1.NextDouble();
      tmp_poses.Add(VectorLF3.zero);
      int num2 = 6;
      int num3 = 8;
      if (num2 < 1)
        num2 = 1;
      if (num3 < 1)
        num3 = 1;
      double num4 = (double) (num3 - num2);
      int num5 = (int) (num1 * num4 + (double) num2);
      for (int index = 0; index < num5; ++index)
      {
        int num6 = 0;
        while (num6++ < 256)
        {
          double num7 = urandom1.NextDouble() * 2.0 - 1.0;
          double num8 = (urandom1.NextDouble() * 2.0 - 1.0) * flatten;
          double num9 = urandom1.NextDouble() * 2.0 - 1.0;
          double num10 = urandom1.NextDouble();
          double d = num7 * num7 + num8 * num8 + num9 * num9;
          if (d <= 1.0 && d >= 1E-08)
          {
            double num11 = Math.Sqrt(d);
            double num12 = (num10 * (maxStepLen - minStepLen) + minDist) / num11;
            VectorLF3 pt = new VectorLF3(num7 * num12, num8 * num12, num9 * num12);
            if (!UniverseGen.CheckCollision(tmp_poses, pt, minDist))
            {
              vectorLf3List.Add(pt);
              tmp_poses.Add(pt);
              if (tmp_poses.Count >= maxCount)
                return;
              break;
            }
          }
        }
      }
      for (int index1 = 1; index1 < 256; ++index1)
      {
        for (int index2 = 0; index2 < vectorLf3List.Count; ++index2)
        {
          if (urandom1.NextDouble() <= 0.7)
          {
            for (int index3 = 1; index3 < 256; ++index3)
            {
              double num13 = urandom1.NextDouble() * 2.0 - 1.0;
              double num14 = (urandom1.NextDouble() * 2.0 - 1.0) * flatten;
              double num15 = urandom1.NextDouble() * 2.0 - 1.0;
              double num16 = urandom1.NextDouble();
              double d = num13 * num13 + num14 * num14 + num15 * num15;
              if (d <= 1.0 && d >= 1E-08)
              {
                double num17 = Math.Sqrt(d);
                double num18 = (num16 * (maxStepLen - minStepLen) + minDist) / num17;
                VectorLF3 pt = new VectorLF3(vectorLf3List[index2].x + num13 * num18, vectorLf3List[index2].y + num14 * num18, vectorLf3List[index2].z + num15 * num18);
                if (!UniverseGen.CheckCollision(tmp_poses, pt, minDist))
                {
                  vectorLf3List[index2] = pt;
                  tmp_poses.Add(pt);
                  if (tmp_poses.Count >= maxCount)
                    return;
                  break;
                }
              }
            }
          }
        }
      }
    }

    private static bool CheckCollision(List<VectorLF3> pts, VectorLF3 pt, double min_dist)
    {
      double num1 = min_dist * min_dist;
      foreach (VectorLF3 pt1 in pts)
      {
        double num2 = pt.x - pt1.x;
        double num3 = pt.y - pt1.y;
        double num4 = pt.z - pt1.z;
        if (num2 * num2 + num3 * num3 + num4 * num4 < num1)
          return true;
      }
      return false;
    }
  }
}
